# ----------------------------------------------------------------------------
#       Copyright (C) 2013-2014 Huynh Vi Lam  <domovilam@gmail.com>
#
#       This file is part of pimucha.
#
#	This program is free software: you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation, either version 3 of the License, or
#	(at your option) any later version.
#	
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#	
#	You should have received a copy of the GNU General Public License
#	along with this program.  If not, see <http://www.gnu.org/licenses/>.
# ----------------------------------------------------------------------------



# ----------------------------------------------------------------------------
# Script for devices and controllers
# with USB interface
# Use python-usb 0.4.3.x
# Python version 2.6.x and 2.7.x
# ----------------------------------------------------------------------------
# pyUSB
# -----
# - Website: https://github.com/walac/pyusb
# - Documentation and source code pyUSB is used as base for development
# ----------------------------------------------------------------------------


import logging,time
import usb


logger = logging.getLogger()


def vidpid(vid):
    try:
        p = vid.split(':')
        return int(p[0],16), int(p[1],16)
    except:
        return None,None

def finddevice(devId):
    vendorId = None
    productId = None
    if devId:
        vendorId, productId = vidpid(devId)
    else:
        return None
    busses = usb.busses()
    for bus in busses:
        for dev in bus.devices:
            if (dev.idVendor == vendorId) and (dev.idProduct == productId):
                return dev
    return None

class DEVusb(object):
    
    def __init__(self,port=None,vid=None):
        self.port = port
        self._id = vid
        self._dev = None
        self._handle = None
        self.opened = False
        self.read_endpoint = 0x81
        self.write_endpoint = 0x02

    def open(self):
        self._dev = finddevice(self._id)
        if self._dev:
            self._handle = self._dev.open()
            logger.warning("USB device found")
            conf = self._dev.configurations[0]        
            self._handle.setConfiguration(conf)
            itf = conf.interfaces[0][0]        
            self._handle.claimInterface(itf)
            self.opened = True
            self.port = 'PortUnknown'
        else:
            logger.critical("No USB device opened")
            self.port = 'PortUnavailable'

    def close(self):
        if not self.opened: return True
        logger.debug("Closing USB device at %s....",self.port)
        self._handle.releaseInterface()
        self.opened = False

    def excepts(self,e):
        if 'timed out' in str(e):
            pass
        elif e.errno in (5,13,16,19):
            # [Errno 5] Input/output error
            # [Errno 13] Access denied (insufficient permissions)
            # [Errno 16] Resource busy
            # [Errno 19] No such device (it may have been disconnected)
            logger.critical("%s : %s", repr(e.__class__), str(e))
            #self.opened = False
            self.close()
        else:
            logger.debug("%s : %s", repr(e.__class__), str(e))


    def read(self, bytes=100):
        """
        Result of read is a sequence of bytes / words in integer format
        """
        if not self.opened: return None
        res = None
        try:
            res = self._handle.bulkRead(self.read_endpoint,bytes)
        except Exception as e:
            self.excepts(e)
        if res:
            logger.debug( "Read %s", ["0x%02x" % i for i in res])
        return res

    def write(self, aSequence, ackbyte=None):
        """
        Write aSequence of 'words' integer format
        for CM15 : ackbyte = 0x55
        """
        if not self.opened: return None
        res = None
        try:
            nb = self._handle.bulkWrite(self.write_endpoint,aSequence)
            if nb == len(aSequence):
                logger.debug("Result %s for writing %s",str(nb),["0x%02x" % i for i in aSequence])
                if not ackbyte: return nb
            else:
                return None
        except Exception as e:
            self.excepts(e)
            return None
        n = 0
        while n < 5:
            res = self.read()
            if res:
               return res
            else:
               time.sleep(0.1)
               n += 1
